﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RobotBattle2.Rsl2;
using RobotBattle2.Scripting;
using System.IO;
using System.Text.RegularExpressions;
using RobotBattle2.Utilities;
using System.CodeDom.Compiler;
using MbUnit.Framework;
using Gallio.Framework;
using Gallio.Common.Collections;

namespace Tests.RobotBattle2.Rsl2 {
	public abstract class ParserTestHarness {
		static readonly Key<int> ParseResultsKey = new Key<int>("ParseFile");
		
		protected Script ParseFile(string input) {
			var context = TestContext.CurrentContext;
			int resultCount = context.Data.GetValueOrDefault<int>(ParseResultsKey, 0) + 1;
			context.Data.SetValue(ParseResultsKey, resultCount);
			var log = context.LogWriter;
			log.AttachPlainText("Code-" + resultCount, input.Trim());
			using (var reader = new StringReader(input)) {
				var script = new Rsl2Parser()
					.LoadScript(reader, context.Test.Name);
				var astWriter = new StringWriter();
				script.Dump(astWriter);
				log.AttachPlainText("Ast-" + resultCount, astWriter.ToString().Trim());
				if (script.Errors.HasErrors) {
					var errorWriter = new StringWriter();
					foreach (var item in script.Errors) {
						errorWriter.WriteLine(item);
					}
					log.AttachPlainText("Errors-" + resultCount, errorWriter.ToString());
					Assert.Fail("Had {0} parse errors", script.Errors.Count);
				}
				return script;
			}
		}

		private Script ParseStatements(string input, out IList<Statement> statements) {
			var script = ParseFile("test(){" + input + "}");
			Assert.AreEqual(1, script.Methods.Count, "Found too many methods");
			statements = script.Methods[0].Statements;
			return script;
		}
		private Script ParseStatement(string input, out Statement statement) {
			IList<Statement> statements;
			var script = ParseStatements(input, out statements);
			Assert.AreEqual(1, statements.Count, "Found too many statements");
			statement = statements[0];
			return script;
		}
		private Script ParseExpression(string input, out Expression expression) {
			ExpressionStatement statement;
			var script = ParseStatement(input + ";", out statement);
			expression = statement.Expression;
			return script;
		}

		protected Expression ParseExpression(string input) {
			Expression expression;
			var script = ParseExpression(input, out expression);
			return expression;
		}
		protected Statement ParseStatement(string input) {
			Statement statement;
			var script = ParseStatement(input, out statement);
			return statement;
		}
		protected IList<Statement> ParseStatements(string input) {
			IList<Statement> statements;
			var script = ParseStatements(input, out statements);
			return statements;
		}

		protected T ParseExpression<T>(string input) where T : Expression {
			Expression value = ParseExpression(input);
			Assert.IsInstanceOfType(typeof(T), value, "Did not get the expected expression type");
			return (T)value;
		}
		protected T ParseStatement<T>(string input) where T : Statement {
			var value = ParseStatement(input);
			Assert.IsInstanceOfType(typeof(T), value, "Did not get the expected statement type");
			return (T)value;
		}

		protected Script ParseExpression<T>(string input, out T expression) where T : Expression {
			Expression value;
			var script = ParseExpression(input, out value);
			Assert.IsInstanceOfType(typeof(T), value, "Did not get the expected expression type");
			expression = (T)value;
			return script;
		}
		protected Script ParseStatement<T>(string input, out T statement) where T : Statement {
			Statement value;
			var script = ParseStatement(input, out value);
			Assert.IsInstanceOfType(typeof(T), value, "Did not get the expected expression type");
			statement = (T)value;
			return script;
		}
	}
}
